package com.zhai.invoke;

import com.alibaba.fastjson.JSONObject;
import com.zhai.config.RpcConfig;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.rpc.service.GenericService;

import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class RpcServiceInvok {

    private static Map<String, Class<?>> builtInMap = new ConcurrentHashMap<>();

    static {
        builtInMap.put(Integer.class.getName(), Integer.class);
        builtInMap.put(Long.class.getName(), Long.class);
        builtInMap.put(Double.class.getName(), Double.class);
        builtInMap.put(Float.class.getName(), Float.class);
        builtInMap.put(Boolean.class.getName(), Boolean.class);
        builtInMap.put(Character.class.getName(), Character.class);
        builtInMap.put(Byte.class.getName(), Byte.class);
        builtInMap.put(Void.class.getName(), Void.class);
        builtInMap.put(Short.class.getName(), Short.class);
        builtInMap.put(String.class.getName(), String.class);

        builtInMap.put(int.class.getName(), int.class);
        builtInMap.put(long.class.getName(), long.class);
        builtInMap.put(double.class.getName(), double.class);
        builtInMap.put(float.class.getName(), float.class);
        builtInMap.put(boolean.class.getName(), boolean.class);
        builtInMap.put(char.class.getName(), char.class);
        builtInMap.put(byte.class.getName(), byte.class);
        builtInMap.put(void.class.getName(), void.class);
        builtInMap.put(short.class.getName(), short.class);
    }

    /**
     * 实现泛化调用
     *
     * @param rpcConfig
     * @param className
     * @param methodName
     * @param parameterTypes
     * @param parameterValues
     * @param returnType
     * @return
     * @throws ClassNotFoundException
     */
    public String rpcGenericInvok(RpcConfig rpcConfig
            , String className
            , String methodName
            , String[] parameterTypes
            , String[] parameterValues
            , String returnType) throws ClassNotFoundException {
        ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
        // 当前dubbo consumer的application配置，不设置会直接抛异常
        ApplicationConfig applicationConfig = new ApplicationConfig();
        applicationConfig.setName(rpcConfig.getApplicationName());
        // 注册中心配置
        RegistryConfig registryConfig = new RegistryConfig();
        // 注册中心这里需要配置上注册中心协议，例如下面的zookeeper
        registryConfig.setAddress(rpcConfig.getRegistryAddress());
//        registryConfig.setGroup(rpcConfig.getRegistryGroup());
        reference.setApplication(applicationConfig);
        reference.setRegistry(registryConfig);
        // 设置调用的reference属性，下面只设置了协议、接口名、版本、超时时间
        reference.setProtocol(rpcConfig.getProtocolName());
        reference.setInterface(className);
//        reference.setVersion(rpcConfig.getProviderVersion());
//        reference.setGroup(rpcConfig.getProviderGroup());
        reference.setTimeout(10 * 1000);
        // 声明为泛化接口
        reference.setGeneric(true);
        // GenericService可以接住所有的实现
        GenericService genericService = reference.get();
        // 构造复杂参数，下面的示例中，头两个参数为string类型，后一个是一个复杂类型，但都可以通过map构造。

        Class<?>[] typesClz = null;
        Object[] invokeValues = null;
        if (parameterTypes != null) {
            typesClz = new Class<?>[parameterTypes.length];
            invokeValues = new Object[parameterValues.length];
            for (int i = 0; i < parameterTypes.length; i++) {
                String typeClassName = parameterTypes[i];
                if ("".equals(typeClassName)) {
                    // skip
                } else if (builtInMap.containsKey(typeClassName)) {
                    typesClz[i] = builtInMap.get(typeClassName);
                    invokeValues[i] = parameterValues[i];
                } else {
                    typesClz[i] = Class.forName(typeClassName);
                    invokeValues = formParameters(typesClz, parameterValues);
                }
            }
        }

        Object invokeData = genericService.$invoke(methodName
                , Arrays.stream(typesClz).map(Class::getName).toArray(String[]::new)
                , invokeValues);
        JSONObject resultData = new JSONObject();
        if (builtInMap.containsKey(returnType)) {
            resultData.put("returnTypeBasic", true);
            resultData.put("invokeData", invokeData);
        } else {
            resultData.put("returnTypeBasic", false);
            JSONObject invokeJson = (JSONObject) JSONObject.toJSON(invokeData);
            resultData.put("invokeData", invokeJson);
        }
        return resultData.toJSONString();
    }

    /**
     * @param types
     * @param values
     * @return
     * @throws ClassNotFoundException
     */
    public Object[] formParameters(Class<?>[] types, String[] values) throws ClassNotFoundException {
        if (types == null || types.length == 0) {
            return new Object[0];
        }
        Object[] valueObjs = new Object[types.length];
        if (values == null) {
            valueObjs = new Object[0];
        } else {
            for (int i = 0; i < values.length; i++) {
                JSONObject dataJson = JSONObject.parseObject(values[i]);
                valueObjs[i] = JSONObject.toJavaObject(dataJson, types[i]);
            }
        }
        return valueObjs;
    }
}
